require 'rubygems'
require 'watir-webdriver'  # require 'watir' fÃ¼r Windows
load 'login.rb'
load 'classes.rb'
load 'init.rb'
load 'drag.rb'

class AllesVerschicktError < RuntimeError
end
class LetzteBurgError < RuntimeError
end
class EmptyPathError < RuntimeError
end

$first = true

$n = 0

$tile_left_px = 0
$tile_top_px  = 0

$attackable = []
$castles = []

$sorted = []

$player_castles = []

$mycastle

$hash_castles = Hash.new

$attacks_max = 100
$number_of_attacks = $attacks_max # dont change this line!

$castlefolder = 'castle/'
$castlefileend = '.cas'

# time for new attack in seconds 3600s * 4 = 4h
$frequency = 3600*4

def cas_select (id = 0)
		if $browser.span(:text => "OK").present?
			puts "Clearing internal error"
			$browser.span(:text => "OK").click
		end
        $browser.div(:id, 'btn_hab_name').click
        wait(3,false,false)
        $browser.span(:text, $player_castles[id].getName).click
end

def language
        # flag for german language
        if ($browser.a(:title, 'Karte').exists?)
                $langID       = 0
                $language     = 'german'
                $lang_map     = 'Karte'
				$lang_profile = 'Profil'
                $lang_countc  = 'Anzahl der Burgen'
				$lang_castle  = 'Burg'
                $lang_attack  = 'Angreifen'
                $lang_f_cas   = 'Freie Burg'
                $lang_sw          = 'Schwertkaempfer'
                $lang_hk          = 'Handkarren'                
        # flag for english language
        elsif ($browser.a(:title, 'Map').exists?)
                $langID       = 1
                $language     = 'english'
                $lang_map     = 'Map'
				$lang_profile = 'Profile'     ## ???????
                $lang_countc  = 'No. of castles'
				$lang_castle  = 'Castle'
                $lang_attack  = 'Attack'
                $lang_f_cas   = 'Free castle'
                $lang_sw          = 'Swordsman'
                $lang_hk          = 'Handcart'
                $lang_miss		= 'Start selected missions'
        end
end

def farmbot
	while true
		autofarm
		puts '###################'
		puts 'farmed all castles. going to sleep and try again'
		puts '###################'
		wait(60)
	end
end

def autofarm
	init_all
	for i in 0 ... $player_castles.size
		sw = $player_castles[i].sw
		hk = $player_castles[i].hk
		if( [sw,hk].min > 0 )
			puts 'selecting castle ' + $player_castles[i].name
			cas_select(i)
			puts 'begin to farm with ' + [sw,hk].min.to_s + ' farmtroops'
			sleep 2.0
			powerfarm(0,0)
		end
	end
	puts 'im finished!'
end

def powerfarm( startID = 0 , call = 0)

        if startID == 0
                init
        end
        
        $browser.a(:title, $lang_map).click
		
		if $browser.img(:id, 'centerHabitatButtonImage').exists?
			$browser.img(:id, 'centerHabitatButtonImage').click
		end
        
        sleep 3.0
        
        burg = "hab_" + $sorted[0].id
        $browser.div(:id, burg).click
        
        if ($browser.div(:text, $lang_attack).exists?)
                $browser.div(:text, $lang_attack).click
        end     
        
        num_sw = $browser.div(:class => 'noButtonInputCount', :index => 1).text.to_i
        num_hk = $browser.div(:class => 'noButtonInputCount', :index => 6).text.to_i
        
        $max_farm_troops = [num_sw,num_hk].min
        
        for i in startID ... $sorted.size
                if $max_farm_troops == 0 || $number_of_attacks == 0
					if call > 0
						raise AllesVerschicktError
					end
                    break
                end
                lastattacktime = readLastLine($sorted[i].id).to_i
                nextattack = arrivaltime($mycastle,$sorted[i]) - lastattacktime
                if  nextattack >= $frequency
                        puts 'attack castle ' + $sorted[i].id
                        $drag_fin = false
                        drag($sorted[i])
                        wait(1,false,false)
                        until $drag_fin do
                        	wait(1,false,false)
                        end
                        burg = "hab_" + $sorted[i].id
                        $browser.div(:id, burg).click
                        attack2(1,1,$sorted[i].id)
                        $n = 0
                        $max_farm_troops -= 1
						$number_of_attacks -= 1
                        puts 'farm troops remaining : ' + $max_farm_troops.to_s
                end
        end
		$number_of_attacks = $attacks_max
        
        rescue Selenium::WebDriver::Error::UnknownError
        puts 'skipping'
        sleep 2.0
        $n += 1
        if $browser.span(:text => "OK").present?
         puts "Clearing internal error"
         $browser.span(:text => "OK").click
         $n = 0
        end
        powerfarm( startID + 1, 0 )
        rescue Selenium::WebDriver::Error::StaleElementReferenceError
        puts 'error occurred'
        sleep 2.0
        retry
		rescue Watir::Exception::UnknownObjectException
		puts 'element not found'
		$n += 1
		# useful if you want to abort the script by logging out
		if $n == 100
			puts 'element not here. im exiting!'
			return
		end
		powerfarm( startID + 1, 0 )
end

def testdrag(index = 0 )
        if index == 0
                init
        end
        $count = index
                for i in index ... $sorted.size-1
                        drag($sorted[i])
                        burg = "hab_" + $sorted[i].id
                        $browser.div(:id, burg).click
                        $count = $count + 1
                        puts 'succesfully ' + $count.to_s + ' dragged'

                end
                rescue Selenium::WebDriver::Error::UnknownError
                puts 'skipping'
                testdrag( $count )
                rescue Selenium::WebDriver::Error::StaleElementReferenceError
                retry
end

def attack2(sw = 1, hk = 1, castleID = '0') 
        if ($browser.div(:text, $lang_attack).exists?)
                $browser.div(:text, $lang_attack).click
        end
        $browser.text_field(:id, 'unitInput1').set sw
        $browser.text_field(:id, 'unitInput6').set hk
        puts 'attack duration ' + $browser.div(:id => 'attackDuration').text
    time = $browser.div(:class => 'noButtonTransportTime').text.to_s
        attacktime = timeToInt(time)
    day = true #isDay(time)
    #isDay(time) verwenden, wenn man wirklich nur tagsüber angreifen will
        #attack castle only at day
    if day
        $browser.span(:text, $lang_attack).click
                appendLastLine(castleID,attacktime.to_s)
                puts 'castle ' + castleID + ' has been attacked'
        else
                puts 'troops arrive at ' + time + '. we do not attack at night!'
       end
        wait(1,rand=false,out=false)
rescue Selenium::WebDriver::Error::StaleElementReferenceError
        puts "Das war zu lahm"
        wait(2,rand = false, out = false)
        retry        
rescue Selenium::WebDriver::Error::ElementNotVisibleError
        attack2(sw, hk, castleID)
end

def yeehaaw( index = 0, castle_index = 0,power=false )

        if $first 
                initPlayerCastles
                cas_select(castle_index)
                puts "selected castle " + $player_castles[castle_index].getName.to_s
                wait(2,true,false)
                init
                $first = false
                wait(5,false,false)
                farmer = get_troops
                if farmer < 1 
                        raise AllesVerschicktError.new("Alle weg")
                end
                
        end
        if power == true
                i = 1
                index =1
                if $first == false
                        powerfarm(1,1)
                else 
                        powerfarm(0,1)
                end
        else
                for i in (index..$sorted.size-1)
                        burg = "hab_" + $sorted[i].id
                        lastattacktime = readLastLine($sorted[i].id).to_i
                        nextattack = arrivaltime($mycastle,$sorted[i]) - lastattacktime
                        if  nextattack >= $frequency
                                drag($sorted[i])
                                wait(1,false,false)
                                $browser.div(:id, burg).click
                                attack(1,1,$sorted[i].id)
                        else
                                puts "Hier waren wir vor kurzem schon"
                                yeehaaw( index + 1, castle_index)
                        end
                end
        end
        rescue Watir::Exception::UnknownObjectException
                if power == true
                        yeehaaw(0,castle_index,power )  
                end
                yeehaaw( i + 1,castle_index,power )             
        rescue Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
        rescue Selenium::WebDriver::Error::UnknownError
                if burg == nil
                        burg = "nicht initialisiert"
                end
                puts "castle is not attackable"
                wait(2,false,false)
                if power == true
                        yeehaaw(0,castle_index,power )  
                end  
                if i == nil
                	i = 0
                end                   
                yeehaaw( i + 1,castle_index,power )
        rescue AllesVerschicktError
                unless castle_index+1 == $player_castles.size
                        $first = true
                        yeehaaw( 0, castle_index + 1,power)
                else raise LetzteBurgError.new("Ich muss weg.")
                end
end             
        
def angreifen( index = 0, castle_index = 0 )
        for i in (index..$sorted.size-1)
                burg = "hab_" + $sorted[i].id
                drag($sorted[i])
                wait(1,false,false)
                $browser.div(:id, burg).click
                attack(1,1,$sorted[i].id)
        end
        # delete all castles
        $sorted.clear
        rescue Watir::Exception::UnknownObjectException
                angreifen( i + 1 )              
        rescue Selenium::WebDriver::Error::MoveTargetOutOfBoundsError
        rescue Selenium::WebDriver::Error::UnknownError
                puts "castle " + burg + " is not attackable"
                angreifen( i + 1 )


end

def isDay(str)
        str = str[-5..str.length-1]
        h = str[0..1].to_i
        m = str[3..4].to_i
        if (h > 7)
                if( h == 23 && m > 49 )
                        return false
                end
                return true
        end
        return false
end

def readLastLine(castleID)
        if File.exists?($castlefolder + castleID + $castlefileend)
                # read only the last line
                return IO.readlines($castlefolder + castleID + $castlefileend)[-1]
        end
        return ''
end

def appendLastLine(castleID,text)
        if File.exists?($castlefolder + castleID + $castlefileend)
                file = File.open($castlefolder + castleID + $castlefileend, 'a')
                file << "\n"
                file << text
                file.close
        end
end

def timeToInt(time)
        # load language again, just to be sure
        language
        # german time format
        if $langID == 0
                day = time[0..1].to_i
                month = time[3..4].to_i
                year = time[6..9].to_i
                hour = time[11..12].to_i
                minute = time[14..15].to_i
        #english time format
        elsif $langID == 1
                day = time[0..1].to_i
                month = time[3..4].to_i
                year = time[6..9].to_i
                hour = time[11..12].to_i
                minute = time[14..15].to_i      
        end
        t = Time.utc(year,month,day,hour,minute)
        return t.to_i
end


def attack(sw = 1, hk = 1, castleID = '0') 
        first = true
#       internal errors etc. abfangen
#       a = $browser.span(:text, "OK")
#       if internal 
#               a.click
#       end
        
        if ($browser.div(:text, $lang_attack).exists?)
                $browser.div(:text, $lang_attack).click
        end     
        num_sw = $browser.div(:class => 'noButtonInputCount', :index => 1).text.to_i
        num_hk = $browser.div(:class => 'noButtonInputCount', :index => 6).text.to_i
        if (([num_sw,num_hk].min-1) %5 == 0 || ([num_sw,num_hk].min-1) < 5 || first == true)
                puts 'Farmers remaining: ' + ([num_sw,num_hk].min).to_s
        end
        if ([num_sw,num_hk].min == 0) 
                puts 'all troups have been sent out'
                raise AllesVerschicktError.new( "Out of Troops" )
                #raise Error "Keine Truppen mehr"
                #angreifen
                #$browser.image(:src, 'pictures/Icons/CenterHabitat@2x.png').click
                return
        end
        if (num_sw > 0 && num_hk >0)
                $browser.text_field(:id, 'unitInput1').set sw
                $browser.text_field(:id, 'unitInput6').set hk
                puts 'attack duration ' + $browser.div(:id => 'attackDuration').text
        time = $browser.div(:class => 'noButtonTransportTime').text.to_s
                attacktime = timeToInt(time)
                lastattacktime = readLastLine(castleID).to_i
                nextattack = attacktime - lastattacktime
        day = true #isDay(time)
        #isDay(time) verwenden, wenn man wirklich nur tagsüber angreifen will
                # attack castle only at day
        if day && nextattack >= $frequency
            $browser.span(:text, $lang_attack).click
                        appendLastLine(castleID,attacktime.to_s)
                        puts 'castle ' + castleID + ' has been attacked'
                elsif nextattack < $frequency
                        puts 'castle ' + castleID + ' has been attacked recently'
                else
                        puts 'troops arrive at ' + time + '. we do not attack at night!'
        end
        end
        wait(2,rand=false,out=false)
        first = false
rescue Selenium::WebDriver::Error::StaleElementReferenceError
        puts "Das war zu lahm"
        wait(2,rand = false, out = false)
        retry        
rescue Selenium::WebDriver::Error::ElementNotVisibleError
        attack(sw, hk, castleID,false)
end
def browser #startet Browser und geht auf die lak Seite
        $browser = Watir::Browser.new :chrome
        puts "visiting website"
        $browser.goto('http://www.lordsandknights.com')
end

def wait(time=rand(3),rand=true,out=true)
        #wait $time seconds
        if(rand == true)
                wait = rand(time)+rand()/10.0
        else 
                wait = time + rand()/3.0
        end
        if (out == true) 
                puts "waiting for " + ((wait*100).round/100.0).to_s + ' s' 
        end
        sleep wait.to_f
end


#loggt sich mit angegebener email und passwort ein, default = globale variablen
def login(useremail=$useremail,userpw=$userpw) 
        #open the login
        wait(1,true,false)
        puts "opening login"
        $browser.link(:id, 'openLoginButton').click  
        wait(5,true,false)

        #enter login data
        puts 'entering userdata & pw'
        $browser.text_field(:name => 'loginEmail').set useremail
        $browser.text_field(:name => 'loginPassword').set userpw

        #log in
        $browser.div(:id => 'loginbutton').click
        sleep 2.0

        #select world
        puts 'selecting world'
        $browser.span(:text => 'Deutsch 5').click
        puts 'log in successful'
        language
        puts 'language is set to ' +$language
end



# return the string between str1 and str2 of line
def findString(line,str1,str2)
        i = line.index(str1)
        j = line.index(str2,i)
        return line[i+str1.length..j-1]
end




def silver
	$browser.a(:title, $lang_castle).click
	sleep 1
	$browser.a(:title, $lang_castle).click
	sleep 1
	$browser.a(:class, "keep {buildingID: '5'}").click
	sleep 1
	$browser.img(:src, 'pictures/Resources/CopperIcon@2x.png').click
	sleep 1
	$browser.img(:src, 'pictures/Browser/Clickable_Arrow@2x.png').click
end

def eukldistance(burg1,burg2)
        x = (burg1.getx - burg2.getx)
        y = (burg1.gety - burg2.gety)
        return Math.sqrt(x*x+y*y)
end

def eukldistancepixel(burg1,burg2)
        x = (burg1.getxpixel -	 burg2.getxpixel)
        y = (burg1.getypixel - burg2.getypixel)
        return Math.sqrt(x*x+y*y)
end

def hexdistance(burg1,burg2)
	x = (burg1.getx - burg2.getx)
	y = (burg1.gety - burg2.gety)
	return (x.abs+y.abs+(x-y).abs)/2
	# see http://jmz.iki.fi/blog/programming/hexagonal_maps
end

def extract
        #$browser.a(:title, $lang_map).click
        
        wait(5)
        
        htmlfile = $browser.html
        
        ############
        #only write html file for debugging
        f = File.open("html.txt", 'w')
        f.puts htmlfile
        f.close
end

def sign(x)
        #puts ((x.abs)/2).floor.to_i
        if x >= 0
                return 1
        elsif x < 0
                return -1
        end
        return 0
end

def abshalf(x)
        return ((x.abs)/2).floor.to_i
end

#berechnet ankunftszeit selbst anstatt auf die Website zu warten
#sollte abfrage der nur alle x Stunden deutlich verschnellern
def arrivaltime(burg1, burg2)
		dist = hexdistance(burg1,burg2)
        arriv_time = Time.now.to_i + (dist*(13*60+20)).to_i
        return arriv_time
end

def get_troops
        sw, hk = false,false
        i = 0
        #until sw && hk 
        #       if ($browser.div(:class => "noButtonLabelUnits",:index =>i).text == $lang_sw)
        #               sw = true
                        num_sw = $browser.div(:class => "noButtonCountUnits",:index =>1).text.to_i
        #       end
        #       if ($browser.div(:class => "noButtonLabelUnits",:index =>i).text == $lang_hk)
        #               hk = true
                        num_hk = $browser.div(:class => "noButtonCountUnits",:index =>5).text.to_i
        #       end
        #       i += 1
        #end
        return [num_sw,num_hk].min
end

def path
	$greedy = []
	length = 30
	number=$sorted.size/length
	for i in (0..number) do
		sort = $sorted[(0+i*length)..(length*(i+1)-1)]
		initial = sort.shift
		$greedy << initial
		nearest = []
		for i in (1..length) do
			sort( sort, $greedy[$greedy.size-1], nearest )
			$greedy << nearest.shift
		end
	end
	$greedy.delete(nil)
end


# inefficient sorting O(n^2)
def sort(castles = $castles, mycastle = $mycastle, sorted = $sorted)
		temp = []
		for i in 0 ... castles.size
			temp << castles[i]
		end
        for i in 0 ... castles.size
                min = hexdistance(castles[0],mycastle)
                index = 0
                for j in 0 ... castles.size
                        dist = hexdistance(castles[j],mycastle)
                        if( dist < min )
                                min = dist
                                index = j
                        end
                end
                sorted << castles[index]
                castles.delete_at(index)
        end
		# useful for later use
		for i in 0 ... temp.size
			castles << temp[i]
		end
		
end

$init = false

def attack_only( ownCastleID )
	i = ownCastleID
	unless $init_all
		init_all
		$init_all = true
	end
	farm_troops = [$player_castles[i].hk, $player_castles[i].sw].min
	if farm_troops == 0
		return
	end
	cas_select(i)
	wait(1,false,false)
	$browser.a(:title, $lang_map).click
	wait(1,false,false)
	$browser.image(:src => /CenterHabitat/).click
	wait(1,false,false)
	unless $init
		init
		path
		$init = true
		wait(1,false,false)
	end
	current_castle = $mycastle
	wait(1,false,false)
	if ($number == 0 || $number == nil)
		$number = farm_troops
	end
	count = 0
	while $greedy.size > 0 && farm_troops > 0 && $number > count do
		lastattacktime = readLastLine($greedy[0].id.to_s).to_i
        nextattack = arrivaltime($mycastle,$greedy[0]) - lastattacktime
		if  nextattack >= $frequency
			puts 'attack castle ' + $greedy[0].id
            drag_ft($greedy[0],current_castle)
            wait(1,false,false)
            burg = "hab_" + $greedy[0].id
            $browser.div(:id, burg).click
            attack2(1,1,$greedy[0].id)
            count +=1
            $player_castles[i].hk -= 1
            $player_castles[i].sw -= 1
            $n = 0
            farm_troops = [$player_castles[i].hk, $player_castles[i].sw].min
            puts 'farm troops remaining : ' + farm_troops.to_s
			current_castle = $greedy.shift	
		else $greedy.shift
		end
	end	
	$init = false
	rescue Selenium::WebDriver::Error::UnknownError
		$n += 1
        if $n == 5 && $browser.span(:text => "OK").present?
         puts "Clearing internal error"
         $browser.span(:text => "OK").click
         $n = 0
        end
		$browser.image(:src => /CenterHabitat/).click
		$greedy.shift        
		attack_only( i )
	rescue Watir::Exception::UnknownObjectException	
		$n += 1
        if $browser.span(:text => "OK").present?
         puts "Clearing internal error"
         $browser.span(:text => "OK").click
         $n = 0
        else 
         $greedy.shift
         $n = 0
        end
		$browser.image(:src => /CenterHabitat/).click
		$greedy.shift        
		attack_only( i )
	rescue Selenium::WebDriver::Error::StaleElementReferenceError
		$browser.image(:src => /CenterHabitat/).click
		$greedy.shift
		attack_only( i )	
	rescue Selenium::WebDriver::Error::StaleElementReferenceError
		$browser.refresh
		$browser.a(:title, $lang_map).click
		$browser.image(:src => /CenterHabitat/).click		
		$browser.image(:src => /CenterHabitat/).click
		$greedy.shift
		attack_only( i )
	rescue Timeout::Error
		$browser.close
		browser
		login
		$init = false
		$init_all = false
		init_all
		attack_only( i )					
		
end

def attack_inf
	i = 0
	while true do
		attack_only( i%($player_castles.size-1))
		i +=1
	end
end
$init_all = false


def limit_attack( number, list=[], ownCastleID = 0 )
	$number = number
	$list = list
	new_attack( ownCastleID )
end

def no_limits
	$number = 0
	$list = []	
end

def new_attack( ownCastleID = 0 )
	unless $init_all
		init_all
		$init_all = true
	end
	if ownCastleID >= $player_castles.size
		puts "wie doof"
		return
	end
	for i in (ownCastleID..$player_castles.size-1) do
		#puts i
		if ($list == [] || $list == nil)
			attack_only( i )
		elsif $list.include?(i)
			puts "castle in list"
			attack_only( i )
		else
			puts "castle not in list, skipping"
		end		
	end
	$init_all =false
	rescue Selenium::WebDriver::Error::UnknownError
		$n += 1
        if $n == 5 && $browser.span(:text => "OK").present?
         puts "Clearing internal error"
         $browser.span(:text => "OK").click
         $n = 0
        end
		$browser.image(:src => /CenterHabitat/).click
		$greedy.shift        
		new_attack( i )
	rescue Watir::Exception::UnknownObjectException	
		$n += 1
        if $browser.span(:text => "OK").present?
         puts "Clearing internal error"
         $browser.span(:text => "OK").click
         $n = 0
        else 
         $greedy.shift
         $n = 0
        end
		$browser.image(:src => /CenterHabitat/).click
		$greedy.shift        
		new_attack( i )
	rescue Selenium::WebDriver::Error::StaleElementReferenceError
		$browser.image(:src => /CenterHabitat/).click
		$greedy.shift
		new_attack( i )
end




def missionen
	language
	for i in (0..$player_castles.size-1) do
		cas_select(i)
		$browser.a(:title => $lang_castle).click
		$browser.a(:title => $lang_castle).click
		wait(1,false,false)
		$browser.a(:class => /tavern/).click
		missions = $browser.elements(:class => 'checkbox_missions').size
		for j in (1..missions+5)
			k = missions + 5 -j
			if $browser.checkbox(:id => 'ch_'+j.to_s ).exists?
				if $browser.checkbox(:id => 'ch_'+j.to_s ).visible?
					$browser.checkbox(:id => 'ch_'+j.to_s ).set
				end
			end
		end
		wait(2,false,false)
		$browser.span(:text => $lang_miss).click
		wait(3,false,false)
	end

end	



def testsize
	width  = $browser.element(:id,'map_border').style('width')
	height = $browser.element(:id,'map_border').style('height')
	location = $browser.element(:id, 'map_border').wd.location
	puts location[0].to_s + '/' + location[1].to_s
	puts 'size:   ' + width.to_s + ' / ' + height.to_s
end
def click(castle)
	burg = "hab_" + castle.id
    $browser.div(:id, burg).click
end

def testclick
	location = $browser.element(:id, "bg_16724_16098").wd.location
	puts location[0]
	puts location[1]
	#$browser.image(:id, "bg_16724_16098").click
	#$browser.image(:id, "bg_16724_16098").fire_event "onmouseover"
	#api = Win32API.new('user32','MessageBox',['L', 'P', 'P', 'L'],'I') 
	#api.call(0,'test','titel',0) 
	#SetCursorPos = Win32API.new('user32','SetCursorPos', 'II', 'I')
	#SendInput = Win32API.new('user32','SendInput', 'IPI', 'I')
end